#!/usr/bin/env python
# -*- coding: utf-8 -*-

# @todo: Unsupported methods:
#         getAllResourceNetworkStatus(xs:string username, xs:string password, )
#         getAllResourceStatisticsResult(xs:string username, xs:string password, )
#         getOperatingSystems(xs:string username, xs:string password, )
#         getRegion(xs:string username, xs:string password, )
#         getResources(xs:string resourceRequire, xs:string username, xs:string password, )

from xml.dom import minidom
from suds.client import Client
import sys, os

import wangan.c2appgate.utils as Utils
import wangan.c2appgate.appgateconfig as WSConfig
import wangan.c2appgate.logger as Logger

class VPRPClientError(Exception):
    pass

logger = Logger.initLog('c2appgate.log', 3, True)

WS_METHODS_DICT = {'register_app': 'register',
                   'submit_app': 'submit',
                   'query': 'queryJobProgress',
                   'kill': 'killJob'
                   }

def _get_ws_client(url):
    client = Client(url)
    return client

#@todo: handle exception
def _get_invoke_opt(client, optname):
    return getattr(client.service, optname)

def _do_register_invoke(client, invoke_opt, options):
    '''
    @note: register(xs:string appName, xs:string appDisplayName, appFileArray apps, \
                xs:string username, xs:string password, )
    '''    
    app_fname = options['filename']
    script_fname = options['script_file']
    try:
        app_fcontent = file(os.path.expanduser(options['filename']), 'rb').read()
        script_fcontent = file(os.path.expanduser(options['script_file']), 'rb').read()
    except Exception, e:
        logger.critical('invalid FILE name specified, exit.')
        sys.exit(1)
        
    # construct complex param.
    apps = client.factory.create('appFileArray')
    app_f = client.factory.create('appFile')
    
    app_f.appContent = Utils.b64encode_file(app_fcontent)
    app_f.appFileMd5Value = Utils.get_md5Value(app_fcontent)
    app_f.scriptContent = Utils.b64encode_file(script_fcontent)
    app_f.scriptMd5Value = Utils.get_md5Value(script_fcontent)
    #
    app_f.appFileName = os.path.split(app_fname)
    app_f.scriptFileName = os.path.split(script_fname)
    #
    apps.item.append(app_f)
    params = WSConfig.generate_app_register_dict()
    params['appName'] = options['appname']
    params['appDisplayName'] = options['disname']
    #
    params['apps'] = apps

    ret = invoke_opt(**params)
    
    return ret

#@param 
#    job_params: list, for <parameters> tag
# 
def _do_submit_invoke(client, invoke_opt, job_params, options):
    '''
    @note:  submit(xs:string appId, xs:string userJobId, xs:string parameter, xs:string resultAddress, xs:int jobPriority, \
                xs:int copyNumber, xs:long budgetRunTime, xs:int resourceProvince, xs:int ispName, xs:string bandwidth, \
                xs:string username, xs:string password, )
    '''
    job_id = options['jobid']
    app_id = options['appid']
    if app_id:
        try:
            app_id = file(WSConfig.APPID_FILE, 'r').read()
        except IOError, e:
            logger.critical('failed to open {APPID_FILE}, exit.')
            sys.exit(1)
    #
    params = WSConfig.generate_job_submit_dict(job_params)    
#     params['userJobId'] = job_id
    params['appID'] = app_id
    
    ret = invoke_opt(**params)
    return ret
    
#@param opt: string, operation name
def _do_jobopt_invoke(client, invoke_opt, opt, options):
    '''
    @note: killJob(xs:string userJobId, xs:string username, xs:string password, )
    '''
    job_id = options['jobid']
    params = WSConfig.generate_job_opt_dict()
    params['userJobId'] = job_id 
    
    ret = invoke_opt(**params)
    return ret

def _do_jobquery_invoke(client, invoke_opt, options):
    '''
    @note: queryJobProgress(xs:string userJobId, xs:string username, xs:string password, )
    '''
    job_id = options['jobid']

def do_soapinvoke(suds_client, opt, options, args):
    ''' do_soapinvoke
    @note: 
    '''
    invoke_opt = _get_invoke_opt(suds_client, opt)
    if opt == WS_METHODS_DICT['register_app']:
        ret = _do_register_invoke(suds_client, invoke_opt, options)
    elif opt == WS_METHODS_DICT['submit_app']:
        job_params = args
        ret = _do_submit_invoke(suds_client, invoke_opt, job_params, options)
    elif opt == WS_METHODS_DICT['kill'] or opt == WS_METHODS_DICT['query']:
        ret = _do_jobopt_invoke(suds_client, invoke_opt, opt, options)
    # sth. else operation
    else:
        pass
    #
    _parse_invoke_ret(opt, ret)
#
def _parse_invoke_ret(opt, ws_ret):
    
    if opt == WS_METHODS_DICT['register_app']:
        # extract returned app. id and flush into FILE = app_id
        try:
            xml_doc = minidom.parseString(ws_ret)
            name = xml_doc.getElementsByTagName('name')[0].firstChild.data
            value = xml_doc.getElementsByTagName("value")[0].firstChild.data
            if name.strip() == 'sucess':
                fd = file(WSConfig.APPID_FILE, 'w')
                fd.write('%s' % value.strip())
                fd.close()                
#                 print >> sys.stdout, 'job REGISTER invoke success. %s' % value
                logger.info('job REGISTER invoke success. %s', value)
            else:
                raise VPRPClientError, 'open {APPID_FILE} failed'
        except Exception, e:
            msg = 'Parse REGISTER invoke exception, exit as: %s' % ws_ret
            logger.critical(msg)
            sys.exit(1) 
        
    elif opt == WS_METHODS_DICT['submit_app']:
        # extract returned info.
        try:
            xml_doc = minidom.parseString(ws_ret)
            jid = xml_doc.getElementsByTagName('id')[0].firstChild.data
            retAddr = xml_doc.getElementsByTagName('resultAddr')[0].firstChild.data
            
            tids = xml_doc.getElementsByTagName('taskId')
            taskids = []
            for id in tids:
                taskids.append(id.firstChild.data.strip())
            #@todo: store info.
            
#             print >> sys.stdout, 'job SUBMIT invoke success. %s' % ws_ret
            logger.info('job SUBMIT invoke success. %s', ws_ret)
        except Exception, e:
            msg = 'Parse SUBMIT invoke exception, exit as: %s' % ws_ret
            logger.critical(msg)
            sys.exit(1)
    elif opt == WS_METHODS_DICT['kill']:
        try:
            xml_doc = minidom.parseString(ws_ret)
            name = xml_doc.getElementsByTagName('name')[0].firstChild.data
            if name == 'sucess':
                #
#                 print >> sys.stdout, 'job KILL invoke success.'
                logger.info('job KILL invoke success.')
            else:
                error = xml_doc.getElementsByTagName("value")[0].firstChild.data
        except Exception, e:
            msg = 'job KILL invoke error as: %s' % error
            logger.critical(msg)
            sys.exit(1)           
            
    elif opt == WS_METHODS_DICT['query']:
        try:
            xml_doc = minidom.parseString(ws_ret)
            result = xml_doc.getElementsByTagName('result')[0].firstChild.data
            if result == 'success':
                progress = xml_doc.getElementsByTagName('progress'[0]).firstChild.data
                #@todo: extract task info.           

                logger.info('job QUERY invoke success, working progress: %s', progress)        
            else:
                error = xml_doc.getElementsByTagName('cause')[0].firstChild.data
                raise VPRPClientError, error
        except Exception, e:
            msg = 'Parse SUBMIT invoke exception, exit as: %s' % ws_ret
            logger.critical(msg)
            sys.exit(1)

def _do_opt():
    from optparse import OptionParser
    
    usage = ''' Client script for VPRP (Virtual Public Resource Platform). \n 
    usage: %prog -r [-f FILE] [-s SCRIPT_FILE] [[-n NAME] [-dn DISNAME]]
           %prog -s [-id APPID] [-jid JOBID] [File1 File2 ...]
           %prog -q [-jid JOBID]
           %prog -k [-jid JOBID]
                      
           %prog -h
    '''
    parser = OptionParser(usage)
    
    # register application
    parser.add_option('-r', '--register', dest = 'register_app',
                      action = 'store_true', default = False,
                      help = 'Register new application.')
    # submit job
    parser.add_option('-s', '--submit', dest = 'submit_app',
                      action = 'store_true', default = False,
                      help = 'Submit new job')
    # query job process
    parser.add_option('-q', '--query', dest = 'query_job',
                      action = 'store_true', default = False,
                      help = 'Query job process')
    # kill job
    parser.add_option('-k', '--kill', dest = 'kill_job',
                      action = 'store_true', default = False,
                      help = 'Kill job')
    # params: -r
    parser.add_option('-f', '--file', dest = 'filename',
                      action = 'store', default = '',
                      help = 'Application file ball', metavar = 'FILE')
    parser.add_option('-s', '--script', dest = 'script_file',
                      action = 'store', default = '',
                      help = 'Script file')
    parser.add_option('-n', '--appname', dest = 'appname',
                      action = 'store', default = '{application_name}',
                      help = 'Application name')
    parser.add_option('-dn', '--disname', dest = 'disname',
                      action = 'store', default = '{application_name_display}',
                      help = 'Application display name')
    # params: -s
    parser.add_option('-id', '--appid', dest = 'appid',
                      action = 'store', default = '',
                      help = 'Application id returned by REGISTER')
    parser.add_option('-jid', '--jobid', dest = 'jobid',
                      action = 'store', default = '',
                      help = 'Job id')
    # other else
    parser.add_option('-D', '--debug', dest = 'debug',
                      action = 'store_true', default = False,
                      help = 'Enable debug mode')
    
    #
    (opts, args) = parser.parse_args()
    return opts, args

def _do_main(opts, args):
    if opts.register_app:
        if not opts.filename or not opts.script_file:
            msg = 'Pls. check application file or script file specified.'
            print >> sys.stderr, msg
            sys.exit(1)
        else:
            #
            client = _get_ws_client(WSConfig.GATEWAY_URL)
            do_soapinvoke(client, WS_METHODS_DICT['register_app'], opts, args)
    else:
        client = _get_ws_client(WSConfig.GATEWAY_URL)
        if opts.submit_app:
            do_soapinvoke(client, WS_METHODS_DICT['submit_app'], opts, args)
        elif opts.query_job:
            do_soapinvoke(client, WS_METHODS_DICT['query'], opts, args)
        elif opts.kill_job:
            do_soapinvoke(client, WS_METHODS_DICT['kill'], opts, args)
        # invalid opt.
        else:
            
            pass

def main():
    opts, args = _do_opt()
    _do_main(opts, args)   

#
if __name__ == '__main__':
    
#     opts, args = _do_opt()
#     _do_main(opts, args)
    main()

